/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package elaborate.tag_analysis.client.graph;

import elaborate.tag_analysis.client.actions.reachability.Cluster;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 *
 * @author lendle
 */
public class ClosureClusterBasedGraph implements Graph, Cloneable{
    private Map<String, Cluster> clusters=new HashMap<String, Cluster>();
    private Map<Object, String> point2ClusterMap=new HashMap<Object, String>();
    private List<Object> points=new ArrayList();
    
    private static final String delim="~!@#$%^&*()";//delim between src and target
    private Map<String, String> directConnections=new HashMap<String, String>();//save direct connections, keys are src+delim+target
    
    public List<Cluster> getClusters(){
        return Arrays.asList(clusters.values().toArray(new Cluster[0]));
    }
    
    @Override
    public List<Object> getPoints() {
        return new ArrayList(points);
    }

    @Override
    public void setConnected(Object src, Object target) {
        directConnections.put(src+delim+target, "");
        Cluster srcCluster=this.getCluster(src);
        Cluster targetCluster=this.getCluster(target);
        if(srcCluster.equals(targetCluster)){
            //src and target in the same Cluster, they are already connected
            return;
        }
        else{
            //merge the two clusters, add target to src
            this.clusters.remove(targetCluster.getId());
            for(Object point : targetCluster.getClusteredPoints()){
                srcCluster.getClusteredPoints().add(point);
                this.point2ClusterMap.put(point, srcCluster.getId());
            }
        }
    }
    
    public boolean isDirectlyConnected(Object src, Object target){
        return this.directConnections.containsKey(src+delim+target) || this.directConnections.containsKey(target+delim+src);
    }
    
    private Cluster getCluster(Object point){
        String id=this.point2ClusterMap.get(point);
        if(id==null){
            id=UUID.randomUUID().toString();
            this.point2ClusterMap.put(point, id);
            Cluster cluster=new Cluster();
            cluster.setId(id);
            cluster.getClusteredPoints().add(point);
            this.clusters.put(id, cluster);
        }
        return this.clusters.get(id);
    }

    @Override
    public boolean isConnected(Object src, Object target) {
        Cluster srcCluster=this.getCluster(src);
        Cluster targetCluster=this.getCluster(target);
        return srcCluster.equals(targetCluster);
    }

    //only support undirected graph
    @Override
    public boolean isDirected() {
        return false;
    }

    @Override
    public Graph clone() throws CloneNotSupportedException {
        ClosureClusterBasedGraph cloned=(ClosureClusterBasedGraph) super.clone();
        cloned.clusters.putAll(clusters);
        cloned.point2ClusterMap.putAll(this.point2ClusterMap);
        cloned.points.addAll(points);
        return cloned;
    }

    @Override
    public void addPoint(Object point) {
        this.getCluster(point);
        this.points.add(point);
    }
}
